﻿//-----------------------------------------------------------------------
// <copyright file="IdentityRepositoryBase.cs" company="FastNET Services">
//     Copyright (c) 2007-2011. All Rights Reserved.
// </copyright>
// <warranty>
//     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
//     BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
//     DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </warranty>
//-----------------------------------------------------------------------
#region Usings

using System;
using System.Data.EntityClient;
using System.Data.Objects;
using System.Linq;
using FastNet.RepositorySystem.Interfaces;

#endregion

namespace FastNet.RepositorySystem
{
    /// <summary>
    /// Base Abstract class for a entity framework 4 based identity repository
    /// </summary>
    /// <typeparam name="TIdentityData">The RepositoryData interface for the associated object</typeparam>
    /// <typeparam name="TObject">The Entity Framework object being managed by this repository</typeparam>
    /// <typeparam name="TIdentityType">The underlying Primary Key Type</typeparam>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")]
    public abstract class IdentityRepositoryBase<TIdentityData, TObject, TIdentityType>
        : RepositoryBase<TIdentityData, TObject>, IIdentityRepository<TIdentityData>
        where TIdentityData : IIdentityData
        where TIdentityType : struct, IEquatable<TIdentityType>
        where TObject : class, TIdentityData
    {
        #region CTor/DTor

        /// <summary>
        /// Initializes a new instance of the RepositoryBase class
        /// </summary>
        /// <param name="connection">EntityConnection used for connecting to the database</param>
        protected IdentityRepositoryBase(EntityConnection connection)
            : base(connection)
        {
        }

        #endregion

        #region Repository Members

        /// <summary>
        /// Retrieves the requested <typeparamref name="TObject"/> by an Identity
        /// </summary>
        /// <param name="id">Identity to retrieve the data by</param>
        /// <returns>IData instance or null if one was not found</returns>
        /// <exception cref="InvalidCastException">Thrown if the identity is of the wrong type for this data source</exception>
        public TIdentityData Retrieve(Identity id)
        {
            //This method must be built using generics in mind. LINQ to Entities does not directly support non-primitive types.
            //Because of this, we specify the correct underlying type in the parameter of our custom query.
            if (!id.IsInternalType(typeof(TIdentityType)) && !id.IsConvertible(typeof(TIdentityType)))
                throw new InvalidCastException("Specified Identity contains the wrong underlying type for this data source");

            using (ObjectContext entities = new ObjectContext(Connection))
            {
                ObjectQuery<TObject> query = entities.CreateObjectSet<TObject>().Where("it.Id = @primaryKey");

                query.Parameters.Add(new ObjectParameter("primaryKey", id.UnderlyingType()));
                query.Parameters["primaryKey"].Value = id;

                return query.SingleOrDefault();
            }
        }

        #endregion
    }
}
